package store.lunangangster.bpmn.converter;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import store.lunangangster.bpmn.constant.BpmnXmlConstant;
import store.lunangangster.bpmn.converter.parser.DefinitionsParser;
import store.lunangangster.bpmn.converter.parser.MessageParser;
import store.lunangangster.bpmn.converter.parser.ProcessParser;
import store.lunangangster.bpmn.converter.parser.SubProcessParser;
import store.lunangangster.bpmn.exception.BpmnConvertException;
import store.lunangangster.bpmn.model.BpmnModel;
import store.lunangangster.bpmn.model.FlowElement;
import store.lunangangster.bpmn.model.FlowElementsContainer;
import store.lunangangster.bpmn.model.FlowNode;
import store.lunangangster.bpmn.model.Process;
import store.lunangangster.bpmn.model.SequenceFlow;
import store.lunangangster.bpmn.model.SubProcess;

/**
 * Bpmn的Xml文件的转换器，其综合了{@link BaseElementConverter}和parser。
 * 用于读取xml文件输入流，并且转换为{@link BpmnModel}
 *
 * @author cwk
 * @version 1.0
 */
public class BpmnXmlConverter implements BpmnXmlConstant {
  protected static final DefinitionsParser definitionsParser = new DefinitionsParser();
  protected static final ProcessParser processParser = new ProcessParser();
  protected static final MessageParser messageParser = new MessageParser();
  protected static final SubProcessParser subProcessParser = new SubProcessParser();

  // 用于解析process中的子标签
  protected static Map<String, BaseElementConverter> baseConverterMap = new HashMap<>();


  static {
    // Event
    addConverter(new StartEventConverter());
    addConverter(new EndEventConverter());
    addConverter(new BoundaryEventConverter());

    // IntermediateEvent
    addConverter(new IntermediateCatchEventConverter());
    addConverter(new IntermediateThrowEventConverter());

    // Activity
    addConverter(new TaskConverter());
    addConverter(new UserTaskConverter());

    // Gateway
    addConverter(new ExclusiveGatewayConverter());
    addConverter(new InclusiveGatewayConverter());
    addConverter(new ParallelGatewayConverter());
    addConverter(new EventBasedGatewayConverter());

    // SequenceFlow
    addConverter(new SequenceFlowConverter());

  }

  /**
   * 主要的转换方法，读取Bpmn的xml输入流，转换为BpmnModel
   * @param is    文件输入流
   * @return      BpmnModel对象
   */
  public BpmnModel convert(InputStream is) {
    try {
      XMLInputFactory xif = XMLInputFactory.newInstance();
      XMLStreamReader xsr = xif.createXMLStreamReader(is);
      return convert(xsr);
    } catch (Exception e) {
      throw new BpmnConvertException(e);
    }
  }

  /**
   * 主要的转换方法，读取Bpmn的xml输入流，转换为BpmnModel
   * @param xsr   文件输入流
   * @return      BpmnModel对象
   */
  public BpmnModel convert(XMLStreamReader xsr) {
    try {
      BpmnModel bpmnModel = new BpmnModel();
      // 解析的Bpmn文件中，是否存在process节点
      Process activityProcess = null;
      // 解析的Bpmn文件中，是否存在subProcess节点
      // 使用List来表示嵌套父子结构
      List<SubProcess> nestSubProcesses = null;
      while (xsr.hasNext()) {
        xsr.next();
        // pop subProcess

        if (xsr.isEndElement()) {
          if (xsr.getLocalName().equals(ELEMENT_SUBPROCESS)) {
            postProcessFlowElement(nestSubProcesses.get(nestSubProcesses.size() - 1));
            nestSubProcesses.remove(nestSubProcesses.size() - 1);
          } else if (xsr.getLocalName().equals(ELEMENT_PROCESS)) {
            // 解析完成之后，将FlowNode与SequenceFlow进行绑定
            postProcessFlowElement(activityProcess);
          }
        }
        // 每解析完一个标签，下次获取到的可能是标签与标签之间的换行和空格
        if (!xsr.isStartElement()) {
          continue;
        }

        String localName = xsr.getLocalName();
        if (ELEMENT_DEFINITIONS.equals(localName)) {
          definitionsParser.parse(xsr, bpmnModel);
        } else if (ELEMENT_MESSAGE.equals(localName)) {
          messageParser.parse(xsr, bpmnModel);
        } else if (ELEMENT_PROCESS.equals(localName)) {
          activityProcess = processParser.parse(xsr, bpmnModel);
        } else if (ELEMENT_SUBPROCESS.equals(localName) || ELEMENT_TRANSACTION.equals(localName)) {
          SubProcess subProcess = subProcessParser.parse(xsr, activityProcess, nestSubProcesses);
          if (nestSubProcesses == null) {
            nestSubProcesses = new ArrayList<>();
            nestSubProcesses.add(subProcess);
          }
        } else {
          // 进入此分支，说明开始解析FlowElement节点
          if (activityProcess != null) {
            if (baseConverterMap.containsKey(localName)) {
              BaseElementConverter converter = baseConverterMap.get(localName);
              converter.convertAndAddToBpmnModel(xsr, bpmnModel, nestSubProcesses);
            }
          }
        }
      }

      return bpmnModel;
    } catch (Exception e) {
      throw new BpmnConvertException(e);
    }
  }

  private void postProcessFlowElement(FlowElementsContainer container) {
    for (FlowElement flowElement : container.getFlowElements().values()) {
      if (flowElement instanceof SequenceFlow) {
        // set SequenceFlow Target Element
        SequenceFlow sequenceFlow = (SequenceFlow) flowElement;
        FlowNode targetFlowNode =  (FlowNode) getFlowElementFromProcess(container, sequenceFlow.getTargetRef());
        FlowNode sourceFlowNode = (FlowNode) getFlowElementFromProcess(container, sequenceFlow.getSourceRef());
        sequenceFlow.setTarget(targetFlowNode);

        // set FlowNode incoming and outgoing
        targetFlowNode.getIncoming().add(sequenceFlow);
        sourceFlowNode.getOutgoing().add(sequenceFlow);
      } else {
        // deal with others
      }
    }
  }

  private FlowElement getFlowElementFromProcess(FlowElementsContainer container, String id) {
    return container.getFlowElements().get(id);
  }

  private static void addConverter(BaseElementConverter converter) {
    baseConverterMap.put(converter.getTargetName(), converter);
  }
}
